#include <libdswav/dswvideo_codec_onvif.h>

#define CHECK_ISNEW_EXTCAM_TIMEOUT 10

static pthread_mutex_t m_nFFmpegLock;
static bool m_bLockInit = false;

CDswvideo_codec_onvif::CDswvideo_codec_onvif(LPST_ENV_T pEnv, int nMemSize)
: CDswmemlistbase(pEnv)
, m_pMemory(NULL)
{
	LOG4CPLUS_DEBUG(log_dsw, "Create CDswvideo_codec_onvif class!");
	m_pMemory = new CDswmemory(1024 * 64, nMemSize);
	memset(&m_nExtcamVideo, 0, sizeof(m_nExtcamVideo));
	for (int i = 0; i < MAX_CHANNEL_REMOTE; i++){
		m_nExtcamVideo[i].pCaller = this;
		m_nExtcamVideo[i].nChId = i;
		m_nExtcamVideo[i].nThreadID = INVALID_PTHREAD_ID;
		pthread_mutex_init(&m_nExtcamVideo[i].nLock, NULL);
	}

	av_register_all();
	avcodec_register_all();
	avformat_network_init();
	av_lockmgr_register(CDswvideo_codec_onvif::cbFFmpegLock);

	InitList();
}

CDswvideo_codec_onvif::~CDswvideo_codec_onvif()
{
	for (int i = 0; i < MAX_CHANNEL_REMOTE; i++){
		if (m_nExtcamVideo[i].nThreadID != INVALID_PTHREAD_ID)
			pthread_join(m_nExtcamVideo[i].nThreadID, NULL);
		pthread_mutex_destroy(&m_nExtcamVideo[i].nLock);
	}
	avformat_network_deinit();
	UnInitList();
	if (m_pMemory)
		delete m_pMemory;
	LOG4CPLUS_DEBUG(log_dsw, "Destroy CDswvideo_codec_onvif class!");
}

int CDswvideo_codec_onvif::cbFFmpegLock(void** mutex, enum AVLockOp op)
{
	switch(op){
	case AV_LOCK_CREATE:
		if (!m_bLockInit){
			pthread_mutex_init(&m_nFFmpegLock, NULL);
			m_bLockInit = true;
		}
		*mutex= ((void *)(&m_nFFmpegLock));
		break;
	case AV_LOCK_OBTAIN:
		pthread_mutex_lock((pthread_mutex_t*)*mutex);
		break;
	case AV_LOCK_RELEASE:
		pthread_mutex_unlock((pthread_mutex_t*)*mutex);
		break;
	case AV_LOCK_DESTROY:
		pthread_mutex_destroy((pthread_mutex_t*)*mutex);
		break;
	}
	return 0;
}

void CDswvideo_codec_onvif::InitList()
{
	int nCh;
	for (nCh = 0; nCh < MAX_CHANNEL_REMOTE; nCh++){
		for (int nSubCh = 0; nSubCh < MAX_V_BITSTREAM_NUM; nSubCh++){
			pthread_mutex_init(&m_nUnUsed[nCh * MAX_V_BITSTREAM_NUM + nSubCh].lock, NULL);
			pthread_mutex_init(&m_nUsed[nCh * MAX_V_BITSTREAM_NUM + nSubCh].lock, NULL);
			m_nUnUsed[nCh * MAX_V_BITSTREAM_NUM + nSubCh].seq = 
				m_nUsed[nCh * MAX_V_BITSTREAM_NUM + nSubCh].seq = 0;
			for (int i = 0; i < MAX_VIDEO_FRAMES; i++){
				LPST_VIDEO_FRAME_T pFrame = new ST_VIDEO_FRAME_T;
				memset(pFrame, 0, sizeof(ST_VIDEO_FRAME_T));
				PutNodeToList((void*)pFrame, &m_nUnUsed[nCh * MAX_V_BITSTREAM_NUM + nSubCh]);
			}
		}
	}
	for (nCh = 0; nCh < MAX_CHANNEL_REMOTE; nCh++)
		pthread_mutex_init(&m_nCapNodeList_lock[nCh], NULL);
	pthread_mutex_init(&m_nCapNodeList_UnUsed_lock, NULL);
	for (int i = 0; i < MAX_ALARM_NODE_NUM; i++){
		LPST_ALARM_NODE_T pNode = new ST_ALARM_NODE_T;
		memset(pNode, 0, sizeof(ST_ALARM_NODE_T));
		pNode->pJpegData = new char[MAX_SNAPHSOT_LEN];
		m_nCapNodeList_UnUsed.push_back(pNode);
	}
}

void CDswvideo_codec_onvif::UnInitList()
{
	int nCh;
	for (nCh = 0; nCh < MAX_CHANNEL_REMOTE; nCh++){
		for (int nSubCh = 0; nSubCh < MAX_V_BITSTREAM_NUM; nSubCh++){
			LPST_VIDEO_FRAME_T pNode = NULL;
			pNode = (LPST_VIDEO_FRAME_T)GetNodeFromList(&m_nUnUsed[nCh * MAX_V_BITSTREAM_NUM + nSubCh]);
			while(pNode){
				if (pNode->pFrame)
					m_pMemory->DswMemoryDelete(pNode->pFrame);
				delete pNode;
				pNode = (LPST_VIDEO_FRAME_T)GetNodeFromList(&m_nUnUsed[nCh * MAX_V_BITSTREAM_NUM + nSubCh]);
			}
			pNode = (LPST_VIDEO_FRAME_T)GetNodeFromList(&m_nUsed[nCh * MAX_V_BITSTREAM_NUM + nSubCh]);
			while(pNode){
				if (pNode->pFrame)
					m_pMemory->DswMemoryDelete(pNode->pFrame);
				delete pNode;
				pNode = (LPST_VIDEO_FRAME_T)GetNodeFromList(&m_nUsed[nCh * MAX_V_BITSTREAM_NUM + nSubCh]);
			}
			pthread_mutex_destroy(&m_nUsed[nCh * MAX_V_BITSTREAM_NUM + nSubCh].lock);
			pthread_mutex_destroy(&m_nUnUsed[nCh * MAX_V_BITSTREAM_NUM + nSubCh].lock);
		}
	}
	CLEAR_ALARM_NODE(m_nCapNodeList_UnUsed);
	pthread_mutex_destroy(&m_nCapNodeList_UnUsed_lock);
	for (nCh = 0; nCh < MAX_CHANNEL_REMOTE; nCh++){
		CLEAR_ALARM_NODE(m_nCapNodeList[nCh]);
		pthread_mutex_destroy(&m_nCapNodeList_lock[nCh]);
	}
}

LPST_ALARM_NODE_T CDswvideo_codec_onvif::GetCapNode(LPLIST_ST_ALARM_NODE_T pList, pthread_mutex_t *pLock)
{
	LPST_ALARM_NODE_T pNode = NULL;
	MUTEX_LOCK(pLock);
	if (!pList->empty()){
		pNode = pList->front();
		pList->pop_front();
	}
	MUTEX_UNLOCK(pLock);
	return pNode;
}

void CDswvideo_codec_onvif::PutCapNode(LPST_ALARM_NODE_T pNode, LPLIST_ST_ALARM_NODE_T pList, pthread_mutex_t *pLock)
{
	MUTEX_LOCK(pLock);
	pList->push_back(pNode);
	MUTEX_UNLOCK(pLock);
}

void CDswvideo_codec_onvif::ResetList(LPST_LIST_MEM_T pListSrc, LPST_LIST_MEM_T pListDst)
{
	LPST_VIDEO_FRAME_T pNode = (LPST_VIDEO_FRAME_T)GetNodeFromList(pListSrc);
	while(pNode != NULL){
		if (pNode->pFrame)
			m_pMemory->DswMemoryDelete(pNode->pFrame);
		pNode->pFrame = NULL;
		PutNodeToList(pNode, pListDst);
		pNode = (LPST_VIDEO_FRAME_T)GetNodeFromList(pListSrc);
	}
	pListSrc->seq = 0;
}

int CDswvideo_codec_onvif::InterruptSubStream(LPST_EXTCAM_VIDEO_T pExtcamVideo)
{
	return (time(NULL) - pExtcamVideo->nLastTm) > MAX_AV_RECV_TIMEOUT;
}

bool CDswvideo_codec_onvif::IsNewExtCam(LPST_EXTCAM_VIDEO_T pExtcamVideo)
{
	bool bIsNew = false;
	if ((pExtcamVideo->nLastTm % CHECK_ISNEW_EXTCAM_TIMEOUT) == 0){
		MUTEX_LOCK(&pExtcamVideo->nLock);
		bIsNew = pExtcamVideo->bNewExtcam;
		pExtcamVideo->bNewExtcam = false;
		MUTEX_UNLOCK(&pExtcamVideo->nLock);
	}
	return bIsNew;
}

bool CDswvideo_codec_onvif::IsDisableExtCam(LPST_EXTCAM_VIDEO_T pExtcamVideo)
{
	bool bIsDisable = false;
	if ((pExtcamVideo->nLastTm % CHECK_ISNEW_EXTCAM_TIMEOUT) == 0){
		MUTEX_LOCK(&pExtcamVideo->nLock);
		bIsDisable = !pExtcamVideo->nExtcam.bEnabled;
		MUTEX_UNLOCK(&pExtcamVideo->nLock);
	}
	return bIsDisable;
}

void CDswvideo_codec_onvif::PutVideoFrame(int64_t nDTS, LPST_EXTCAM_VIDEO_T pExtcamVideo, LPST_LIST_MEM_T pListDst, LPST_LIST_MEM_T pListSrc)
{
	LPST_PACKET_RECV_BUF_T pPacketBuf = &pExtcamVideo->nPacketBuf;
	if ((pPacketBuf->pPos - pPacketBuf->pBuffer) > V_BITSTREAM_LEN){
		LOG4CPLUS_ERROR(log_dsw, "External CH[" << pExtcamVideo->nChId << "] streams [" << CAP_EXTCAM_STREAM << "] Invalid frame length!");
		return;
	}
	LPST_VIDEO_FRAME_T pFrame = (LPST_VIDEO_FRAME_T)GetNodeFromList(pListSrc);
	if (pFrame == NULL){
EX_RE_FREE:							
		pFrame = (LPST_VIDEO_FRAME_T)GetNodeFromList(pListDst);
	}
	if (pFrame->pFrame){
		m_pMemory->DswMemoryDelete(pFrame->pFrame);
		pFrame->pFrame = NULL;
	}
	pFrame->pFrame = m_pMemory->DswMemoryNew(pPacketBuf->pPos - pPacketBuf->pBuffer);
	if (!pFrame->pFrame){
		LOG4CPLUS_ERROR(log_dsw, "External CH[" << pExtcamVideo->nChId  << "] streams [" << CAP_EXTCAM_STREAM << "] has not enough frame memory! bs len = " << 
			pPacketBuf->pPos - pPacketBuf->pBuffer);
		PutNodeToList(pFrame, pListSrc);
		goto EX_RE_FREE;
	}
	m_pMemory->DswMemoryCpy((char*)pPacketBuf->pBuffer, pPacketBuf->pPos - pPacketBuf->pBuffer, pFrame->pFrame);

	pFrame->ch_num = pExtcamVideo->nChId;
	pFrame->sub_num = CAP_EXTCAM_STREAM;

	pFrame->nExtCamWidth = pExtcamVideo->nExtcam.nStreamInfo[CAP_EXTCAM_STREAM].nWidth;
	pFrame->nExtCamHeight = pExtcamVideo->nExtcam.nStreamInfo[CAP_EXTCAM_STREAM].nHeight;
	pFrame->nFps = pExtcamVideo->nExtcam.nStreamInfo[CAP_EXTCAM_STREAM].nFps;
	pFrame->is_newbs = false;
	pFrame->is_newfps = false;

	if ((memcmp(pPacketBuf->pBuffer, "\x00\x00\x00\x01", 4) == 0) && (memcmp(&pPacketBuf->pBuffer[1], "\x00\x00\x00\x01", 4) != 0) && ((pPacketBuf->pBuffer[4] & 0x1f) == 0x07))
		pFrame->is_keyframe = true;
	else
		pFrame->is_keyframe = false;

	if (pFrame->is_keyframe){
		pFrame->nExtCamWidth = pExtcamVideo->nExtcam.nStreamInfo[CAP_EXTCAM_STREAM].nWidth;
		pFrame->nExtCamHeight = pExtcamVideo->nExtcam.nStreamInfo[CAP_EXTCAM_STREAM].nHeight;
		pFrame->nFps = pExtcamVideo->nExtcam.nStreamInfo[CAP_EXTCAM_STREAM].nFps;
	}
	MUTEX_LOCK(&pListDst->lock);
	pFrame->seq = pListDst->seq++;
	MUTEX_UNLOCK(&pListDst->lock);
	pFrame->length = pPacketBuf->pPos - pPacketBuf->pBuffer;

	gettimeofday(&pFrame->timestamp, NULL);

	pFrame->nDTS = nDTS;
#if 0
	struct timeval tv;
	gettimeofday(&tv, NULL);
	unsigned long ntv = tv.tv_usec / 1000;
	ntv += nDTS / 90;
	tv.tv_sec += ntv / 1000;
	tv.tv_usec = ntv % 1000 * 1000;
	pFrame->timestamp.tv_sec = tv.tv_sec;
	pFrame->timestamp.tv_usec = tv.tv_usec;
//	pFrame->timestamp.tv_sec = (unsigned long)((nDTS / 90) / 1000);
//	pFrame->timestamp.tv_usec = (unsigned long)(((nDTS / 90) % 1000) * 1000);
#endif

	PutNodeToList(pFrame, pListDst);
}

bool CDswvideo_codec_onvif::GetVideoFrame(LPST_VIDEO_FRAME_T pFrame)
{
	int nCh = pFrame->ch_num;
	int nSubCh = pFrame->sub_num;
	return CopyNodeFromList(&m_nUsed[nCh * MAX_V_BITSTREAM_NUM + nSubCh], (void*)pFrame, TYPE_VIDEO_FRAME);
}

bool CDswvideo_codec_onvif::DumpFrame(void* pSrc, void* pDest)
{
	LPST_VIDEO_FRAME_T pSrcFrame = (LPST_VIDEO_FRAME_T)pSrc;
	LPST_VIDEO_FRAME_T pDestFrame = (LPST_VIDEO_FRAME_T)pDest;
	if (pSrcFrame->length > pDestFrame->length)
		return false;
	int nCpBytes = m_pMemory->DswMemoryCpy(pSrcFrame->pFrame, (char*)pDestFrame->pFrame, pDestFrame->length);
	memcpy(pDestFrame, pSrcFrame, sizeof(ST_VIDEO_FRAME_T) - sizeof(void*));
	return (nCpBytes == pSrcFrame->length);
}

#if 0
void CDswvideo_codec_onvif::ProcessCapture(int nCh, AVCodecContext *pCodecCtx, AVFrame *pFrame, AVPacket *pPacket, LPLIST_ST_ALARM_NODE_T pList, pthread_mutex_t *pLock)
{
	LPST_ALARM_NODE_T pAlarmNode = NULL;
	bool bDropCap = false;
	bool bHasAlarmNode = false;
	char sFileName[128];
	MUTEX_LOCK(pLock);
	if (!pList->empty()){
		pAlarmNode = pList->front();
		pList->pop_front();
		if (abs(pAlarmNode->nTrigTime - time(NULL)) > 30)
			bDropCap = true;
		else{
			bHasAlarmNode = true;
			pList->push_back(pAlarmNode);
		}
	}
	MUTEX_UNLOCK(pLock);
	if (bDropCap)
		PutCapNode(pAlarmNode, &m_nCapNodeList_UnUsed, &m_nCapNodeList_UnUsed_lock);
	else if (bHasAlarmNode){
		int nFinished;
		pFrame = av_frame_alloc();
		avcodec_decode_video2(pCodecCtx, pFrame, &nFinished, pPacket);
		if ((nFinished) && CapJpeg(nCh, pFrame, pCodecCtx->width, pCodecCtx->height, &pCodecCtx->time_base, sFileName)){
			FILE* pFp = fopen(sFileName, "rb");
			if (pFp){
				do{
					pAlarmNode = NULL;
					bHasAlarmNode = false;
					MUTEX_LOCK(pLock);
					if (!pList->empty()){
						pAlarmNode = pList->front();
						pList->pop_front();
						bHasAlarmNode = !pList->empty();
					}
					MUTEX_UNLOCK(pLock);
					if (pAlarmNode){
						fseek(pFp, 0, SEEK_SET);
						pAlarmNode->nPayloadSize = fread(pAlarmNode->pJpegData, 1, MAX_SNAPHSOT_LEN, pFp);
						pAlarmNode->nPayloadType = EN_PIC;
						LOG4CPLUS_DEBUG(log_dsw, "Alarm capture pic [ " << nCh << " ] finished, and send message to server client!");
						SendMessage(PMSG_SET_ALARMCAP_FINISHED, (void*)pAlarmNode);
						PutCapNode(pAlarmNode, &m_nCapNodeList_UnUsed, &m_nCapNodeList_UnUsed_lock);
					}
				}while(bHasAlarmNode);
				fclose(pFp);
				remove(sFileName);
			}
			else	{
				do{
					pAlarmNode = NULL;
					bHasAlarmNode = false;
					MUTEX_LOCK(pLock);
					if (!pList->empty()){
						pAlarmNode = pList->front();
						pList->pop_front();
						bHasAlarmNode = !pList->empty();
					}
					MUTEX_UNLOCK(pLock);
					if (pAlarmNode)
						PutCapNode(pAlarmNode, &m_nCapNodeList_UnUsed, &m_nCapNodeList_UnUsed_lock);
				}while(bHasAlarmNode);
				LOG4CPLUS_DEBUG(log_dsw, "Alarm capture pic [ " << nCh << " ] failed!");
			}
		}
		av_frame_free(&pFrame);
		pFrame = NULL;
	}
}
#else
void CDswvideo_codec_onvif::ProcessCapture(int nCh, AVCodecContext *pCodecCtx, AVCodec *pCodec, AVPacket *pPacket, LPLIST_ST_ALARM_NODE_T pList, pthread_mutex_t *pLock)
{
	LPST_ALARM_NODE_T pAlarmNode = NULL;
	bool bDropCap = false;
	bool bHasAlarmNode = false;
	char sFileName[128];
	AVFrame *pFrame;
	MUTEX_LOCK(pLock);
	if (!pList->empty()){
		pAlarmNode = pList->front();
		pList->pop_front();
		if (abs(pAlarmNode->nTrigTime - time(NULL)) > 30)
			bDropCap = true;
		else{
			bHasAlarmNode = true;
			pList->push_back(pAlarmNode);
		}
	}
	MUTEX_UNLOCK(pLock);
	if (bDropCap)
		PutCapNode(pAlarmNode, &m_nCapNodeList_UnUsed, &m_nCapNodeList_UnUsed_lock);
	else if (bHasAlarmNode){
		int nFinished;
		if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
			LOG4CPLUS_ERROR(log_dsw, "Open decoder failed!");
		else{
			pFrame = av_frame_alloc();
			avcodec_decode_video2(pCodecCtx, pFrame, &nFinished, pPacket);
			if ((nFinished) && CapJpeg(nCh, pFrame, pCodecCtx->width, pCodecCtx->height, &pCodecCtx->time_base, sFileName)){
				FILE* pFp = fopen(sFileName, "rb");
				if (pFp){
					do{
						pAlarmNode = NULL;
						bHasAlarmNode = false;
						MUTEX_LOCK(pLock);
						if (!pList->empty()){
							pAlarmNode = pList->front();
							pList->pop_front();
							bHasAlarmNode = !pList->empty();
						}
						MUTEX_UNLOCK(pLock);
						if (pAlarmNode){
							fseek(pFp, 0, SEEK_SET);
							pAlarmNode->nPayloadSize = fread(pAlarmNode->pJpegData, 1, MAX_SNAPHSOT_LEN, pFp);
							pAlarmNode->nPayloadType = EN_PIC;
							LOG4CPLUS_DEBUG(log_dsw, "Alarm capture pic [ " << nCh << " ] finished, and send message to server client!");
							SendMessage(PMSG_SET_ALARMCAP_FINISHED, (void*)pAlarmNode);
							PutCapNode(pAlarmNode, &m_nCapNodeList_UnUsed, &m_nCapNodeList_UnUsed_lock);
						}
					}while(bHasAlarmNode);
					fclose(pFp);
					remove(sFileName);
				}
				else	{
					do{
						pAlarmNode = NULL;
						bHasAlarmNode = false;
						MUTEX_LOCK(pLock);
						if (!pList->empty()){
							pAlarmNode = pList->front();
							pList->pop_front();
							bHasAlarmNode = !pList->empty();
						}
						MUTEX_UNLOCK(pLock);
						if (pAlarmNode)
							PutCapNode(pAlarmNode, &m_nCapNodeList_UnUsed, &m_nCapNodeList_UnUsed_lock);
					}while(bHasAlarmNode);
					LOG4CPLUS_DEBUG(log_dsw, "Alarm capture pic [ " << nCh << " ] failed!");
				}
			}
			avcodec_close(pCodecCtx);
			av_frame_free(&pFrame);
		}
	}
}
#endif

bool CDswvideo_codec_onvif::CapJpeg(int nCh, AVFrame* pFrame, int nWidth, int nHeight, AVRational* pTimebase, char* pFileName)
{
	bool bRet = false;
	sprintf(pFileName, "/var/extcam_%02d.jpg", nCh);
	AVFormatContext* pFormatCtx = avformat_alloc_context();
	pFormatCtx->oformat = av_guess_format("mjpeg", NULL, NULL);
	if (avio_open(&pFormatCtx->pb, pFileName, AVIO_FLAG_READ_WRITE) < 0){
		LOG4CPLUS_ERROR(log_dsw, "Couldn't open " << pFileName  << " file.");
		goto END_CAP;
	}
	else{
		AVStream* pAVStream = avformat_new_stream(pFormatCtx, 0);
		AVCodecContext* pCodecCtx = pAVStream->codec;
		pCodecCtx->codec_id = pFormatCtx->oformat->video_codec;
		pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
		pCodecCtx->pix_fmt = AV_PIX_FMT_YUVJ420P;
		pCodecCtx->width = nWidth;
		pCodecCtx->height = nHeight;
		pCodecCtx->time_base.num = pTimebase->num;
		pCodecCtx->time_base.den = pTimebase->den;

		AVCodec* pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
		if (!pCodec)
			LOG4CPLUS_ERROR(log_dsw, "Couldn't find codec.");
		else{
			if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
				LOG4CPLUS_ERROR(log_dsw, "Couldn't open codec.");
			else{
				int ret = avformat_write_header(pFormatCtx, NULL);
				int y_size = pCodecCtx->width * pCodecCtx->height;
				AVPacket pkt;
				av_new_packet(&pkt, y_size * 3);
				int got_picture = 0;
				ret = avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_picture);
				if (ret < 0)
					LOG4CPLUS_ERROR(log_dsw, "Encoder error.");
				else if (got_picture == 1){
					av_write_frame(pFormatCtx, &pkt);
					bRet = true;
				}
				av_packet_unref(&pkt);
				av_write_trailer(pFormatCtx);
			}
		}
		if (pAVStream)
			avcodec_close(pAVStream->codec);
		avio_close(pFormatCtx->pb);
	}
END_CAP:
	avformat_free_context(pFormatCtx);
	return bRet;
}

void CDswvideo_codec_onvif::TestCapImage(int nCh, AVCodecContext *pCodecCtx, AVCodec* pCodec, AVFrame *pFrame, AVPacket *pPacket)
{
	int nFinished = 0;
	char sFileName[128];
	pFrame = av_frame_alloc();
#if 1
	int s = avcodec_open2(pCodecCtx, pCodec, NULL);
	printf("ssssssssss = %d\n");
	int k = avcodec_decode_video2(pCodecCtx, pFrame, &nFinished, pPacket);
	if ((nFinished) && CapJpeg(nCh, pFrame, pCodecCtx->width, pCodecCtx->height, &pCodecCtx->time_base, sFileName)){
		printf("jjjjjjjjjjjjjjjjjjjjjjjj %s -> %s\n", sFileName, sFileName);
				char sk[256];
				sprintf(sk, "/var/%d.jpg", time(NULL));
				rename(sFileName, sk);
	}
	avcodec_close(pCodecCtx);
#else
	int k = avcodec_decode_video2(pCodecCtx, pFrame, &nFinished, pPacket);
	if (nFinished){
		int picSize = pCodecCtx->width * pCodecCtx->height;
		int newSize = picSize * 1.5;
		unsigned char *pBuffer = new unsigned char[newSize];
		int a, i;
		a = 0;
		for (i = 0; i < pCodecCtx->height; i++){
			memcpy(pBuffer + a, pFrame->data[0] + i * pFrame->linesize[0], pCodecCtx->width);
			a += pCodecCtx->width;
		}
		for (i = 0; i < pCodecCtx->height / 2; i++){
			memcpy(pBuffer + a, pFrame->data[1] + i * pFrame->linesize[1], pCodecCtx->width / 2);
			a += pCodecCtx->width / 2;
		}
		for (i = 0; i < pCodecCtx->height / 2; i++){
			memcpy(pBuffer + a, pFrame->data[2] + i * pFrame->linesize[2], pCodecCtx->width / 2);
			a += pCodecCtx->width / 2;
		}
		char sName[32];
		sprintf(sName, "jj%d.yuv", s++);
		
		FILE* fp = fopen(sName, "wb");
		fwrite(pBuffer, 1, a, fp);
		fclose(fp);
		delete pBuffer;
		printf("jjjjjjjjjjjjjjjjjjjjj = %s\n", sName);
	}
#endif
	av_frame_free(&pFrame);
}

void CDswvideo_codec_onvif::ExtcamAVRecvThreadFunc(LPST_EXTCAM_VIDEO_T pExtcamVideo)
{
	LOG4CPLUS_DEBUG(log_dsw, "Start ExtcamAVRecvThreadFunc on Ch [" << pExtcamVideo->nChId << "]...");
	AVFormatContext* context;
	int video_stream_index, i;
	AVDictionary* options;
	AVPacket packet;
	ST_EXTERNAL_CAM_T nExtcam[MAX_CHANNEL_REMOTE];

	AVCodec *pCodec;
	AVCodecContext *pCodecCtx;
	AVFrame *pFrame;

	time_t nStartTm;
	int64_t nLastDTS = 0;
	int64_t nBaseDTS;

	time_t nstart;
	
	pExtcamVideo->nPacketBuf.pBuffer = new unsigned char[MAX_PACKET_RECV_BUF];
RETRY_RTSP:
	nStartTm = time(NULL);
	do{
		usleep(1000 * 100);
	}while(!m_pEnv->bExit && (abs(nStartTm - time(NULL)) < 5));
	
	pExtcamVideo->nPacketBuf.pPos = pExtcamVideo->nPacketBuf.pBuffer;
	pExtcamVideo->nPacketBuf.bHasValidFrame = false;

	context = avformat_alloc_context();
	video_stream_index = -1;
	options = NULL;
	av_dict_set(&options, "rtsp_transport", "tcp", 0);
	av_dict_set(&options, "max_delay", "5000000", 0);
	av_dict_set(&options, "probesize", "4096", 0);

	context->interrupt_callback.callback = CDswvideo_codec_onvif::cbInterruptSubStream;
	context->interrupt_callback.opaque = pExtcamVideo;
	pExtcamVideo->nLastTm = time(NULL);

#if 0
	char* pp;
	if (pExtcamVideo->nChId == 0)
		pp = (char*)"rtsp://192.168.1.201:554/1/h264major";
	else if (pExtcamVideo->nChId == 1)
		pp = (char*)"rtsp://192.168.1.118:554/live0.264";
	else if (pExtcamVideo->nChId == 2)
		pp = (char*)"rtsp://192.168.1.212:554/Streaming/Channels/102?transportmode=unicast&profile=Profile_2";
	else
		pp = pExtcamVideo->nExtcam.sStreamURL[CAP_EXTCAM_STREAM];
	if(avformat_open_input(&context, pp, NULL, &options) != 0){
#else
	if(avformat_open_input(&context, pExtcamVideo->nExtcam.sStreamURL[CAP_EXTCAM_STREAM], NULL, &options) != 0){
#endif
		LOG4CPLUS_ERROR(log_dsw, "avformat_open_input failed! URL = " << 
			pExtcamVideo->nExtcam.sStreamURL[CAP_EXTCAM_STREAM] << 
			" Stream index = " << CAP_EXTCAM_STREAM);
		goto END_RECVSTREAM;
	}
	context->max_analyze_duration = AV_TIME_BASE;
	if(avformat_find_stream_info(context, NULL) < 0){
		LOG4CPLUS_ERROR(log_dsw, "avformat_find_stream_info failed! URL = " << 
			pExtcamVideo->nExtcam.sStreamURL[CAP_EXTCAM_STREAM] << 
			" Stream index = " << CAP_EXTCAM_STREAM);
		goto END_RECVSTREAM;
	}
	for(i = 0; i < context->nb_streams; i++){
		if(context->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){
			video_stream_index = i;
			pCodecCtx = context->streams[i]->codec;
			break;
		}
	}
	if (video_stream_index == -1){
		LOG4CPLUS_ERROR(log_dsw, "No video stream! URL = " << 
			pExtcamVideo->nExtcam.sStreamURL[CAP_EXTCAM_STREAM] << 
			" Stream index = " << CAP_EXTCAM_STREAM);
		goto END_RECVSTREAM;
	}
	av_dump_format(context, 0, context->filename, 0);

	pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
	if (pCodec == NULL)
		LOG4CPLUS_ERROR(log_dsw, "Can't find decoder!");
	else if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
		LOG4CPLUS_ERROR(log_dsw, "Open decoder failed!");

	nstart = time(NULL);
	while(!m_pEnv->bExit && !IsNewExtCam(pExtcamVideo) && !IsDisableExtCam(pExtcamVideo) && !pExtcamVideo->bEndThread){
#if 0
		if (abs(nstart - time(NULL)) > 60){
			printf("ssssssssssssss\n");
			nstart = time(NULL);
			ST_ALARM_NODE_T nAlarmNode;
			strcpy(nAlarmNode.sId, "00000001");
			nAlarmNode.nTrigVal = 0x80;
			nAlarmNode.nZoneTrigVal = 0x80;
			nAlarmNode.nTrigTime = nstart;
			strcpy(nAlarmNode.nZone.sId, nAlarmNode.sId);
			nAlarmNode.nZone.nAction = EN_PIC | EN_VIDEO;

			nAlarmNode.nAction = EN_PIC | EN_VIDEO;
			nAlarmNode.nBindV = 0x80;
			printf("kkkkkkkkkkkkkkkkkkkkkkkkk\n");
			OnMessage(PMSG_SET_ALARMCAP_START, &nAlarmNode);
			SendMessage(PMSG_SET_ALARMREC_START, &nAlarmNode);
		}
#endif		
		int av_ret;
		av_ret = av_read_frame(context, &packet);
		if (av_ret >= 0){
			if (packet.stream_index == video_stream_index){
//				printf("ssss = %02x, %02x, %02x, %02x, %02x\n", packet.data[0], packet.data[1], packet.data[2], packet.data[3], packet.data[4]);
//				if ((memcmp(packet.data, "\x00\x00\x00\x01", 4) == 0) && (((packet.data[4] & 0x7f) == 0x67) || ((packet.data[4] & 0x7f) == 0x61))){
//				if ((memcmp(packet.data, "\x00\x00\x00\x01", 4) == 0) && (((packet.data[4] & 0x7f) == 0x67) || ((packet.data[4] & 0x7f) == 0x61) || ((packet.data[4] & 0x7f) == 0x41))){
//printf("pts = %lld, dts = %lld\n", packet.pts, packet.dts);

				if ((memcmp(packet.data, "\x00\x00\x00\x01", 4) == 0) && (((packet.data[4] & 0x60) >> 5) != 0) && ((((packet.data[4] & 0x1f) >> 0) == 7) || (((packet.data[4] & 0x1f) >> 0) == 1))){
					if (pExtcamVideo->nPacketBuf.bHasValidFrame){
/*
						printf("save len = %d, data = %02x, %02x, %02x, %02x, %02x\n",
							pExtcamVideo->nPacketBuf.pPos - pExtcamVideo->nPacketBuf.pBuffer,
							pExtcamVideo->nPacketBuf.pBuffer[0], pExtcamVideo->nPacketBuf.pBuffer[1],
							pExtcamVideo->nPacketBuf.pBuffer[2], pExtcamVideo->nPacketBuf.pBuffer[3],
							pExtcamVideo->nPacketBuf.pBuffer[4]);
*/
#if 0
						if ((time(NULL) % 10) == 0){
							AVPacket packet1;
							packet1.data = pExtcamVideo->nPacketBuf.pBuffer;
							packet1.size = pExtcamVideo->nPacketBuf.pPos - pExtcamVideo->nPacketBuf.pBuffer;
							TestCapImage(pExtcamVideo->nChId, pCodec, pFrame, &packet1);
						}
#endif
						if (pCodec){
							AVPacket packet1;
							packet1.data = pExtcamVideo->nPacketBuf.pBuffer;
							packet1.size = pExtcamVideo->nPacketBuf.pPos - pExtcamVideo->nPacketBuf.pBuffer;
//							ProcessCapture(pExtcamVideo->nChId, pCodecCtx, pFrame, &packet1, &m_nCapNodeList[pExtcamVideo->nChId], &m_nCapNodeList_lock[pExtcamVideo->nChId]);
							ProcessCapture(pExtcamVideo->nChId, pCodecCtx, pCodec, &packet1, &m_nCapNodeList[pExtcamVideo->nChId], &m_nCapNodeList_lock[pExtcamVideo->nChId]);
						}
						if (nLastDTS == 0){
							PutVideoFrame(nLastDTS, pExtcamVideo,
								&m_nUsed[pExtcamVideo->nChId * MAX_V_BITSTREAM_NUM + CAP_EXTCAM_STREAM],
								&m_nUnUsed[pExtcamVideo->nChId * MAX_V_BITSTREAM_NUM + CAP_EXTCAM_STREAM]);
							nLastDTS = packet.pts;
							nBaseDTS = nLastDTS;
						}
						else{
							PutVideoFrame(packet.pts - nBaseDTS, pExtcamVideo,
								&m_nUsed[pExtcamVideo->nChId * MAX_V_BITSTREAM_NUM + CAP_EXTCAM_STREAM],
								&m_nUnUsed[pExtcamVideo->nChId * MAX_V_BITSTREAM_NUM + CAP_EXTCAM_STREAM]);
							nLastDTS = packet.pts;
						}

						pExtcamVideo->nPacketBuf.pPos = pExtcamVideo->nPacketBuf.pBuffer;
						pExtcamVideo->nPacketBuf.bHasValidFrame = false;
					}
					pExtcamVideo->nPacketBuf.bHasValidFrame = true;
				}
				memcpy(pExtcamVideo->nPacketBuf.pPos, packet.data, packet.size);
				pExtcamVideo->nPacketBuf.pPos += packet.size;

			}
		}
		else{
			char sErr[512] = {0};
			av_strerror(errno, sErr, sizeof(sErr));
			LOG4CPLUS_ERROR(log_dsw, "Read packet failed! URL = " << 
				pExtcamVideo->nExtcam.sStreamURL[CAP_EXTCAM_STREAM] << 
				" Stream index = " << CAP_EXTCAM_STREAM << " Err = " << sErr);
			pExtcamVideo->nPacketBuf.pPos = pExtcamVideo->nPacketBuf.pBuffer;
			pExtcamVideo->nPacketBuf.bHasValidFrame = false;
			break;
		}
		av_packet_unref(&packet);
		pExtcamVideo->nLastTm = time(NULL);
	}
END_RECVSTREAM:
	if (pCodec){
		avcodec_close(pCodecCtx);
		pCodecCtx = NULL;
		pCodec = NULL;
	}
	avformat_close_input(&context);

	ResetList(&m_nUsed[pExtcamVideo->nChId * MAX_V_BITSTREAM_NUM + CAP_EXTCAM_STREAM], &m_nUnUsed[pExtcamVideo->nChId * MAX_V_BITSTREAM_NUM + CAP_EXTCAM_STREAM]);
	if (!m_pEnv->bExit){
		bool bfound = false;
		memset(nExtcam, 0, sizeof(nExtcam));
		SendMessage(PMSG_GET_EXTCAMS, &nExtcam);
		for (i = 0; i < MAX_CHANNEL_REMOTE; i++){
			if (pExtcamVideo->nExtcam.nChId == nExtcam[i].nChId){
				memcpy(&pExtcamVideo->nExtcam, &nExtcam[i], sizeof(pExtcamVideo->nExtcam));
				bfound = true;
				break;
			}
		}
		if (IS_ONLINE(pExtcamVideo->nExtcam.nLiveTm) && !IsDisableExtCam(pExtcamVideo))
			goto RETRY_RTSP;
		else{
			if (!(IS_ONLINE(pExtcamVideo->nExtcam.nLiveTm)))
				printf("kkkkkkkk = %d, found = %d, %d, %s\n", __LINE__, bfound, pExtcamVideo->nExtcam.nChId, pExtcamVideo->nExtcam.sStreamURL);
			if (IsDisableExtCam(pExtcamVideo))
				printf("jjjjjjjjjjjjjj = %d, found = %d, %d, %s\n", __LINE__, bfound, pExtcamVideo->nExtcam.nChId, pExtcamVideo->nExtcam.sStreamURL);
		}
	}

	delete pExtcamVideo->nPacketBuf.pBuffer;
	pExtcamVideo->bEndThread = true;
	LOG4CPLUS_DEBUG(log_dsw, "Exit ExtcamAVRecvThreadFunc on Ch [" << pExtcamVideo->nChId << "]...");
}

int CDswvideo_codec_onvif::OnMessage(EN_MSG_TYPE nMsgType, void * pParam)
{
	switch(nMsgType){
	case PMSG_GET_EXTCAM_VIDOE_ISLIVE:
		{
			LPST_EXTERNAL_CAM_T pCam = (LPST_EXTERNAL_CAM_T)pParam;
			int nCh = pCam->nChId;
			MUTEX_LOCK(&m_nExtcamVideo[nCh].nLock);
			memcpy(pParam, &(m_nExtcamVideo[nCh].nExtcam), sizeof(ST_EXTERNAL_CAM_T));
			MUTEX_UNLOCK(&m_nExtcamVideo[nCh].nLock);
			MUTEX_LOCK(&m_nUsed[nCh * MAX_V_BITSTREAM_NUM + CAP_EXTCAM_STREAM].lock);
			if (m_nUsed[nCh * MAX_V_BITSTREAM_NUM + CAP_EXTCAM_STREAM].seq == 0)
				pCam->nLiveTm = 0;
			else
				pCam->nLiveTm = time(NULL);
			MUTEX_UNLOCK(&m_nUsed[nCh * MAX_V_BITSTREAM_NUM + CAP_EXTCAM_STREAM].lock);
		}
		break;
	case PMSG_SET_EXTCAMS:
		{
			LPST_EXTERNAL_CAM_T pExtcam = (LPST_EXTERNAL_CAM_T)pParam;
			int nCh, i;
			bool bFound = false;
			for (nCh = 0; nCh < MAX_CHANNEL_REMOTE; nCh++){
				if ((m_nExtcamVideo[nCh].nThreadID == INVALID_PTHREAD_ID) || (m_nExtcamVideo[nCh].bEndThread))
					continue;
				MUTEX_LOCK(&m_nExtcamVideo[nCh].nLock);
				bFound = false;
				for (i = 0; i < MAX_CHANNEL_REMOTE; i++){
					if (pExtcam[i].nType == EN_EXTCAM_UNKNOW)
						continue;
					if (strcmp(pExtcam[i].sUUID, m_nExtcamVideo[nCh].nExtcam.sUUID) == 0){
						memcpy(&m_nExtcamVideo[nCh].nExtcam, &pExtcam[i], sizeof(m_nExtcamVideo[nCh].nExtcam));
						bFound = true;
						break;
					}
				}
				if (!bFound)
					m_nExtcamVideo[nCh].bEndThread = true;
				MUTEX_UNLOCK(&m_nExtcamVideo[nCh].nLock);
			}
		}
		break;
	case PMSG_SET_EXTCAM_UPDATE_STREAM:
		{
			LPST_EXTERNAL_CAM_T pExtcam = (LPST_EXTERNAL_CAM_T)pParam;
			if (pExtcam->nCapbility & (EXTCAM_CAPBILITY_SUBSTREAM | EXTCAM_CAPBILITY_MAINSTREAM)){
				if ((m_nExtcamVideo[pExtcam->nChId].nThreadID == INVALID_PTHREAD_ID) || (m_nExtcamVideo[pExtcam->nChId].bEndThread)){
					memcpy(&m_nExtcamVideo[pExtcam->nChId].nExtcam, pExtcam, sizeof(ST_EXTERNAL_CAM_T));
					if (m_nExtcamVideo[pExtcam->nChId].bEndThread)
						pthread_join(m_nExtcamVideo[pExtcam->nChId].nThreadID, NULL);
					m_nExtcamVideo[pExtcam->nChId].bEndThread = false;
					pthread_create(&m_nExtcamVideo[pExtcam->nChId].nThreadID, NULL, CDswvideo_codec_onvif::cbExtcamAVRecvThreadFunc, &m_nExtcamVideo[pExtcam->nChId]);
				}
				else {
					MUTEX_LOCK(&m_nExtcamVideo[pExtcam->nChId].nLock);
					m_nExtcamVideo[pExtcam->nChId].bNewExtcam = true;
					memcpy(&m_nExtcamVideo[pExtcam->nChId].nExtcam, pExtcam, sizeof(ST_EXTERNAL_CAM_T));
					MUTEX_UNLOCK(&m_nExtcamVideo[pExtcam->nChId].nLock);
				}
			}
			else
				LOG4CPLUS_WARN(log_dsw, "Extcam [" << pExtcam->sUUID << "] dose not support stream...");
		}
		break;
	case PMSG_GET_VIDEOCODEC_REMOTE_CLASS:
		*((unsigned long*)pParam) = (unsigned long)this;
		break;
	case PMSG_GET_VIDEOFRAME:
		return GetVideoFrame((LPST_VIDEO_FRAME_T)pParam);
		break;
	case PMSG_SET_ALARMCAP_START:
		{
			LPST_ALARM_NODE_T pAlarmNode = (LPST_ALARM_NODE_T)pParam;
			if (pAlarmNode->nBindV & MASK_BINDV_EXTCAM){
				LPST_ALARM_NODE_T pNode = GetCapNode(&m_nCapNodeList_UnUsed, &m_nCapNodeList_UnUsed_lock);
				if (pNode){
					char* pJpegData = pNode->pJpegData;
					memcpy(pNode, pParam, sizeof(ST_ALARM_NODE_T));
					pNode->pJpegData = pJpegData;
					PutCapNode(pNode, &m_nCapNodeList[pAlarmNode->nBindV & ~MASK_BINDV_EXTCAM], &m_nCapNodeList_lock[pAlarmNode->nBindV & ~MASK_BINDV_EXTCAM]);
				}
				else
					LOG4CPLUS_DEBUG(log_dsw, "Not enough alarm cap node! drop this cap zone id [ " << ((LPST_ALARM_NODE_T)pParam)->sId << " ]");
			}
		}
		break;
	}
	return 0;
}

